package mint.modules.generator;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import mint.MintLexer;
import mint.modules.generator.expression.ExprNode;
import mint.modules.generator.expression.Expression;
import mint.modules.generator.expression.ExpressionType;
import mint.modules.symbol.ClassSymbol;
import mint.modules.symbol.ConstructorSymbol;
import mint.modules.symbol.Symbol;
import mint.modules.symbol.SymbolType;
import mint.modules.type.Type;

import org.antlr.runtime.tree.Tree;

public class ExplicitConstructorInvocation extends Scope implements MintNode {
	private int type;
	private List<Expression> arguments;
	private List<String> errors;
	private int line;	
	private ConstructorDeclarationNode ctorDeclaration;

	public ExplicitConstructorInvocation(ConstructorBodyNode theParent, Tree explicitCtorTree) {
		super(theParent, theParent.containerClass);
		Tree argumentTree;
		
		line = explicitCtorTree.getLine();
		errors = new ArrayList<String>();
		arguments = new ArrayList<Expression>();
		type = explicitCtorTree.getChild(0).getType();
		
		argumentTree = explicitCtorTree.getChild(1);
		
		if (argumentTree != null)
			for (int i = 0; i < argumentTree.getChildCount(); i++)
			{
				arguments.add(new ExprNode(theParent, ExpressionType.Right, argumentTree.getChild(i)));
				CounterManager.getInstance().incrementParameter();
			}
		ctorDeclaration = theParent.getConstructorDeclaration();
	}

	@Override
	public void testSemantic() {
		String className = ctorDeclaration.getContainerClass();
		Symbol symbol;
		
		if (type == MintLexer.SUPER)
		{
			symbol = ctorDeclaration.getSuperConstructor();
			ctorDeclaration.setSuperCalled(true);
		}
		else
		{
			symbol = searchSymbol(className, getContainerClass(), false);
			ClassSymbol classSymbol = (ClassSymbol)symbol;				
			symbol = classSymbol.getClassNode().searchSymbol("<init>", getContainerClass(), false);
		}
		
		for (Expression expr : arguments)
			expr.testSemantic();
		if (symbol == null)
		{
			errors.add(line + ": " + className + " doesn't have a constructor definition :(");
		}
		else
		{
			if(symbol.getSymbolType() != SymbolType.Constructor)
				errors.add(line + ": " + className + " doesn't have a constructor definition");
			else
			{
				ConstructorSymbol ctorSymbol = (ConstructorSymbol)symbol;
				
				if (ctorSymbol.getParameterListSize() != arguments.size())
					errors.add(line + ": " + className + " constructor take " + 
								ctorSymbol.getParameterListSize() + " but received " +
								arguments.size() + " arguments");
				else
				{
					for (int i = 0; i < ctorSymbol.getParameterListSize(); i++)
					{
						if (ctorSymbol.getParameter(i).getType().equals(Type.floatType))
						{
							if(!(arguments.get(i).getType().equals(Type.intType) || arguments.get(i).getType().equals(Type.floatType)))
								errors.add(line + ": " + className + " " + i + 
										"th constructor argument expected type: " + ctorSymbol.getParameter(i).getType().getCompleteName()
										+ " received type: " + arguments.get(i).getType().getCompleteName());
						} else if (!ctorSymbol.getParameter(i).getType().equals(arguments.get(i).getType()))
							if(!arguments.get(i).getType().equals(Type.objectNullType))
								errors.add(line + ": " + className + " " + i + 
									"th constructor argument expected type: " + ctorSymbol.getParameter(i).getType().getCompleteName()
									+ " received type: " + arguments.get(i).getType().getCompleteName());
					}
				}
			}
		}
	}

	@Override
	public List<String> getSemanticErrors() {
		for (Expression expr : arguments)
			errors.addAll(expr.getSemanticErrors());
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		int i;
		Symbol symbol;
		String className = ctorDeclaration.getContainerClass();
		
		symbol = searchSymbol(className, getContainerClass(), false);
		ClassSymbol classSymbol = (ClassSymbol)symbol;		
		
		
		if (type == MintLexer.SUPER)
		{
			symbol = ctorDeclaration.getSuperConstructor();
			className = classSymbol.getSuperClass();
		}
		else
		{								
			symbol = classSymbol.getClassNode().searchSymbol("<init>", getContainerClass(), false);
		}
		
		ConstructorSymbol ctorSymbol = (ConstructorSymbol)symbol;
		
		output.format("aload_0\n");
		
		i = 0;
		for (Expression expr : arguments)
		{
			expr.emit(output);
			if (ctorSymbol.getParameter(i).getType().equals(Type.floatType))
				if(expr.getType().equals(Type.intType))
					output.format("i2f\n");
		}
		
		output.format("invokespecial %s/<init>(", className);
		for (i = 0; i < ctorSymbol.getParameterListSize(); i++)
			output.format("%s", ctorSymbol.getParameter(i).getType().emit());
		output.format(")V\n");
	}

	@Override
	public int getLine() {
		return line;
	}

	@Override
	public void printSymbolTable(Formatter output, int tabSize) {}

}
